package cn.iocoder.yudao.module.system.controller.admin.cp;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.cp.enums.ProcessEnum;
import cn.iocoder.yudao.module.system.controller.admin.cp.util.LeaveTimeDetail;
import cn.iocoder.yudao.module.system.controller.admin.cp.util.LeaveTimeSplitter;
import cn.iocoder.yudao.module.system.controller.admin.cp.vo.*;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.feign.ApprovedApi;
import cn.iocoder.yudao.module.system.feign.ApprovedReqVO;
import cn.iocoder.yudao.module.system.feign.ApprovedResp;
import cn.iocoder.yudao.module.system.job.cp.UserDeptJob;
import cn.iocoder.yudao.module.system.service.cp.CpService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.converters.longconverter.LongStringConverter;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.cp.bean.message.WxCpMessageSendResult;
import me.chanjar.weixin.cp.bean.oa.WxCpCheckinData;
import me.chanjar.weixin.cp.bean.oa.WxCpCheckinMonthData;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;

/**
 * @author pg
 * @project zysx
 * @description 同步企微信息
 * @date 2024/2/26 10:14:37
 */
@Tag(name = "管理后台 - 同步企微信息")
@RestController
@RequestMapping("/system/cp")
@Validated
@Slf4j
public class CpController {

    @Resource
    private CpService cpService;

    @Resource
    private AdminUserService userService;

    @Resource
    private ApprovedApi approvedApi;

    @Resource
    private UserDeptJob userDeptJob;

    @PostMapping("test")
    @PermitAll
    public CommonResult<Boolean> test() throws WxErrorException {
        userDeptJob.syncUserDept();
        return success(true);
    }


    @PostMapping("dept")
    @Operation(summary = "同步部门信息")
    public CommonResult<Boolean> createDept(@RequestHeader("Tenant-Id") Integer tenantId) {
        if (tenantId == null) {
            return error(new ErrorCode(901, "租户ID错误"));
        }
        return success(cpService.createDept(tenantId));
    }

    @PostMapping("user")
    @Operation(summary = "同步人员信息")
    public CommonResult<Boolean> createUser(@RequestHeader("Tenant-Id") Integer tenantId, @RequestParam("id") Long id) {
        if (tenantId == null) {
            return error(new ErrorCode(901, "租户ID错误"));
        }
        return success(cpService.createUser(tenantId, id));
    }

    @PostMapping("sendMessage")
    @PermitAll
    @Operation(summary = "发送通知消息给企微用户")
    public WxCpMessageSendResult sendMessage(@RequestHeader("Tenant-Id") Integer tenantId, @RequestBody NoticeMessageForm form) throws WxErrorException {
        log.info("sendMessage form:{}", JSON.toJSONString(form));
        if (tenantId == null) {
            WxCpMessageSendResult result = new WxCpMessageSendResult();
            result.setErrCode(901);
            result.setErrMsg("租户ID错误");
            return result;
        }
        return cpService.sendMessage(tenantId, form);
    }

    @PostMapping("punchCorrection")
    @Operation(summary = "打卡")
    public CommonResult<Boolean> sendMessageToAll(@RequestHeader("Tenant-Id") Integer tenantId, @RequestBody @Valid CorrectionReq req) throws WxErrorException {
        if (tenantId == null) {
            return error(new ErrorCode(901, "租户ID错误"));
        }
        cpService.punchCorrection(tenantId, req);
        return success(true);
    }

    @GetMapping("getCheckinMonthData")
    @Operation(summary = "月报数据")
    public CommonResult<List<WxCpCheckinMonthData>> getCheckinMonthData(@RequestHeader("Tenant-Id") Integer tenantId, CheckinMonthDataReqVO reqVO) throws WxErrorException {
        if (tenantId == null) {
            return error(new ErrorCode(901, "租户ID错误"));
        }
        return success(cpService.getCheckinMonthData(tenantId, reqVO));
    }

    @GetMapping("/getCheckInData")
    @Operation(summary = "获取打卡信息")
//    @PreAuthorize("@ss.hasPermission('system:cp:list')")
    public CommonResult<PageResult<CheckinRespVO>> getUserPage(@RequestHeader("Tenant-Id") Integer tenantId, @Valid WxCpCheckinDataReqVO pageReqVO) {
        if (tenantId == null) {
            return error(new ErrorCode(901, "租户ID错误"));
        }
        // 获得用户分页列表
        PageResult<AdminUserDO> pageResult = userService.getUserPage(pageReqVO);
        if (CollUtil.isEmpty(pageResult.getList())) {
            return success(new PageResult<>(pageResult.getTotal()));
        }
        List<CheckinRespVO> respVOS = getCheckinData(tenantId, pageReqVO, pageResult);
        return success(new PageResult<>(respVOS, pageResult.getTotal()));
    }

    @GetMapping("/monthDataExport")
    public void monthDataExport(@RequestHeader("Tenant-Id") Integer tenantId, @Valid WxCpCheckinDataReqVO pageReqVO, HttpServletResponse response) throws IOException {
        if (tenantId == null) {
            log.error("租户ID错误");
            return;
        }
        if (pageReqVO.getDeptId() == null || pageReqVO.getDeptId() <= 0) {
            pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        }
        PageResult<AdminUserDO> pageResult = userService.getUserPage(pageReqVO);
        if (CollUtil.isEmpty(pageResult.getList())) {
            log.info("没有需要导出的数据");
            return;
        }
        String filename = "月报数据-" + DateUtil.format(pageReqVO.getStartTime(), "yyyyMMdd") + "-" + DateUtil.format(pageReqVO.getEndTime(), "yyyyMMdd") + ".xlsx";
        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, StandardCharsets.UTF_8.name()));
        response.setContentType("application/vnd.ms-excel;charset=UTF-8");
        List<CheckinRespVO> respVOS = getCheckinData(tenantId, pageReqVO, pageResult);
        // 创建表头
        List<List<String>> head = createHead(pageReqVO.getStartTime(), pageReqVO.getEndTime());

        // 创建数据
        List<List<Object>> data = createData(respVOS, pageReqVO.getStartTime(), pageReqVO.getEndTime());
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        headWriteCellStyle.setWrapped(false);
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        contentWriteCellStyle.setWrapped(true); // 自动换行
        HorizontalCellStyleStrategy horizontalCellStyleStrategy =
                new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);

        EasyExcelFactory.write(response.getOutputStream())
                .head(head)
                .registerWriteHandler(horizontalCellStyleStrategy)
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 基于 column 长度，自动适配。最大 255 宽度
                .registerConverter(new LongStringConverter())
                .sheet("打卡记录")
                .doWrite(data);

    }

    @PostMapping("selectApproved")
    public CommonResult<List<ApprovedResp>> selectApproved(@Valid @RequestBody ApprovedReqVO vo) {
        return CommonResult.success(approvedApi.selectApproved(vo));
    }

    private List<List<String>> createHead(Date startDate, Date endDate) {
        List<List<String>> head = new ArrayList<>();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        SimpleDateFormat dayOfWeekFormat = new SimpleDateFormat("EEEE", Locale.CHINESE);

        // 姓名列
        List<String> head0 = new ArrayList<>();
        head0.add("姓名");
        head.add(head0);

        // 日期列
        for (Date date = startDate; !date.after(endDate); date = new Date(date.getTime() + 86400000L)) { // 86400000L = 1 day in milliseconds
            String formattedDate = sdf.format(date);
            String dayOfWeek = dayOfWeekFormat.format(date);
            List<String> headDate = new ArrayList<>();
            headDate.add(formattedDate + " " + dayOfWeek);
            head.add(headDate);
        }

        return head;
    }

    private List<List<Object>> createData(List<CheckinRespVO> checkinRespVOList, Date startDate, Date endDate) {
        List<List<Object>> data = new ArrayList<>();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

        for (CheckinRespVO respVO : checkinRespVOList) {
            List<Object> rowData = new ArrayList<>();
            rowData.add(respVO.getUserName());
//            rowData.add(respVO.getUserId());

            Calendar calendar = Calendar.getInstance();
            calendar.setTime(startDate);

            while (!calendar.getTime().after(endDate)) {
                String formattedDate = sdf.format(calendar.getTime());

                CheckinDay checkinDay = respVO.getCheckinDays().stream()
                        .filter(day -> day.getCheckinDays().equals(formattedDate))
                        .findFirst()
                        .orElse(null);

                if (checkinDay != null) {
                    StringBuilder sb = new StringBuilder();
                    if (StringUtils.isNotBlank(checkinDay.getExceptionTypeStart())) {
                        if ("时间异常".equals(checkinDay.getExceptionTypeStart())) {
                            sb.append("迟到:").append(checkinDay.getCheckinStart());
                        } else if ("未打卡".equals(checkinDay.getExceptionTypeStart())) {
                            sb.append("——");
                        } else {
                            sb.append("异常:").append(checkinDay.getExceptionTypeStart());
                        }
                    } else {
                        if (StringUtils.isNotBlank(checkinDay.getCheckinStart())) {
                            sb.append("正常:").append(checkinDay.getCheckinStart());
                        } else {
                            sb.append("——");
                        }

                    }
                    if (StringUtils.isNotBlank(checkinDay.getExceptionTypeEnd())) {
                        if ("时间异常".equals(checkinDay.getExceptionTypeEnd())) {
                            sb.append("\n").append("早退:").append(checkinDay.getCheckinEnd());
                        } else if ("未打卡".equals(checkinDay.getExceptionTypeEnd())) {
                            sb.append("\n——");
                        } else {
                            sb.append("\n异常:").append(checkinDay.getExceptionTypeEnd());
                        }
                    } else {
                        if (StringUtils.isNotBlank(checkinDay.getCheckinEnd())) {
                            sb.append("\n").append("正常:").append(checkinDay.getCheckinEnd());
                        } else {
                            sb.append("\n").append("——");
                        }
                    }
                    if(!CollectionUtils.isEmpty(checkinDay.getLeaveTimeDetails())) {
                        for (LeaveTimeDetail leaveTimeDetail : checkinDay.getLeaveTimeDetails()) {
                            sb.append("\n").append(leaveTimeDetail.getType()).append(":").append(leaveTimeDetail.getStartTime()).append("-").append(leaveTimeDetail.getEndTime())
                                    .append(" ").append(leaveTimeDetail.getDuration()).append("小时");
                        }
                    }
                    rowData.add(sb.toString());
                } else {
                    rowData.add("——");
                }
                // 移动到下一天
                calendar.add(Calendar.DATE, 1);
            }

            data.add(rowData);
        }

        return data;
    }

    private List<CheckinRespVO> getCheckinData(Integer tenantId, WxCpCheckinDataReqVO pageReqVO, PageResult<AdminUserDO> pageResult) {
        List<String> wxIds = pageResult.getList().stream().map(AdminUserDO::getWxUserId).collect(Collectors.toList());
        Map<String, String> userMap = pageResult.getList().stream().collect(Collectors.toMap(AdminUserDO::getWxUserId, AdminUserDO::getNickname));
        List<List<String>> splitLists = new ArrayList<>();
        if (wxIds.size() > 100) {
            // 使用Stream API分割列表
            splitLists = IntStream.range(0, (wxIds.size() + 99) / 100)
                    .mapToObj(i -> wxIds.subList(i * 100, Math.min((i + 1) * 100, wxIds.size()))).collect(Collectors.toList());
        } else {
            splitLists.add(wxIds);
        }
        List<WxCpCheckinData> result = new ArrayList<>();
        splitLists.forEach(user -> {
            try {
                result.addAll(cpService.getCheckInData(tenantId, 1, pageReqVO.getStartTime(), pageReqVO.getEndTime(), user));
            } catch (WxErrorException e) {
                e.printStackTrace();
            }
        });
//        log.info("result:{}", JSON.toJSONString(result));

        // 获取所有人的审批记录
        List<ApprovedResp> approvedResps = getApproved(wxIds);
        // 将所有人的记录按照createId分组
        Map<String, List<ApprovedResp>> groupedApprovedByCreateId = approvedResps.stream()
                .collect(Collectors.groupingBy(ApprovedResp::getCreateId));
        // userId分类后再日期分类
        Map<String,Map<String,List<LeaveTimeDetail>>> leaveTimeMap = new HashMap<>();
        // 遍历approvedRespList
        groupedApprovedByCreateId.forEach(
                (createId, approvedRespList) -> {
                    List<LeaveTimeDetail> leaveTimeDetails = new ArrayList<>();
                    approvedRespList.forEach(approvedResp -> {
                        String startTime = approvedResp.getVacation().getStartTime();
                        String endTime = approvedResp.getVacation().getEndTime();
                        if(StringUtils.isBlank(startTime)){
                            Pattern startTimePattern = Pattern.compile("\"startTime\":\"([^\"]+)\"");
                            Pattern endTimePattern = Pattern.compile("\"endTime\":\"([^\"]+)\"");

                            Matcher startTimeMatcher = startTimePattern.matcher(approvedResp.getVariable());
                            Matcher endTimeMatcher = endTimePattern.matcher(approvedResp.getVariable());

                            if (startTimeMatcher.find()) {
                                startTime = startTimeMatcher.group(1);
                            }
                            if (endTimeMatcher.find()) {
                                endTime = endTimeMatcher.group(1);
                            }
                        }
                        if(StringUtils.isNotBlank(startTime) && StringUtils.isNotBlank(endTime)){
                            // 获取请假时间
                            List<LeaveTimeDetail> details = LeaveTimeSplitter.calculateLeave(startTime, endTime, ProcessEnum.getDescByType(approvedResp.getProcessKey()));
                            leaveTimeDetails.addAll(details);
                        }
                    });
                    // 按照日期分类将leaveTimeDetails转为Map
                    Map<String, List<LeaveTimeDetail>> leaveTimeDetailMap = leaveTimeDetails.stream()
                            .collect(Collectors.groupingBy(LeaveTimeDetail::getCurrentDate));
                    leaveTimeMap.put(createId, leaveTimeDetailMap);
                }
        );

        List<WxCpCheckinDataRespVO> wxCpCheckinDataRespVOS = new ArrayList<>();
        result.forEach(wxCpCheckinData -> {
            WxCpCheckinDataRespVO resp = new WxCpCheckinDataRespVO();
            BeanUtils.copyProperties(wxCpCheckinData, resp);
            resp.setNickname(userMap.get(wxCpCheckinData.getUserId()));
            if (wxCpCheckinData.getCheckinTime() != null) {
                resp.setCheckinDate(new Date(wxCpCheckinData.getCheckinTime() * 1000));
                resp.setSchDate(DateUtil.formatDate(new Date(wxCpCheckinData.getCheckinTime() * 1000)));
            }
            if (wxCpCheckinData.getSchCheckinTime() != null) {
                resp.setSchCheckinDate(new Date(wxCpCheckinData.getSchCheckinTime() * 1000));

            }
            wxCpCheckinDataRespVOS.add(resp);
        });
        List<List<WxCpCheckinDataRespVO>> groupedLists = new ArrayList<>(wxCpCheckinDataRespVOS.stream()
                .collect(Collectors.groupingBy(WxCpCheckinDataRespVO::getUserId))
                .values());
        List<CheckinRespVO> respVOS = new ArrayList<>();
        groupedLists.forEach(list -> {
            CheckinRespVO checkinRespVO = new CheckinRespVO();
            checkinRespVO.setUserName(list.get(0).getNickname());
            checkinRespVO.setUserId(list.get(0).getUserId());
            List<CheckinDay> checkinDays = new ArrayList<>();
            log.info("list:{}", JSON.toJSONString(list));
            List<List<WxCpCheckinDataRespVO>> dayLists = new ArrayList<>(list.stream().collect(Collectors.groupingBy(WxCpCheckinDataRespVO::getSchDate)).values());
            dayLists.forEach(dList -> {
                CheckinDay checkinDay = new CheckinDay();
                checkinDay.setCheckinDays(dList.get(0).getSchDate());

                // 使用 Optional 来保存最早和最迟的打卡记录
                Optional<WxCpCheckinDataRespVO> earliestCheckin = dList.stream()
                        .filter(d -> "上班打卡".equals(d.getCheckinType()))
                        .min(Comparator.comparingLong(WxCpCheckinDataRespVO::getCheckinTime));

                Optional<WxCpCheckinDataRespVO> latestCheckout = dList.stream()
                        .filter(d -> "下班打卡".equals(d.getCheckinType()))
                        .max(Comparator.comparingLong(WxCpCheckinDataRespVO::getCheckinTime));

                // 设置最早的上班打卡记录
                earliestCheckin.ifPresent(d -> {
                    checkinDay.setCheckinStart(DateUtil.formatTime(new Date(d.getCheckinTime() * 1000)));
                    checkinDay.setExceptionTypeStart(d.getExceptionType());
                });

                // 设置最迟的下班打卡记录
                latestCheckout.ifPresent(d -> {
                    checkinDay.setCheckinEnd(DateUtil.formatTime(new Date(d.getCheckinTime() * 1000)));
                    checkinDay.setExceptionTypeEnd(d.getExceptionType());
                });
                if(!CollectionUtils.isEmpty(leaveTimeMap.get(list.get(0).getUserId())) && !CollectionUtils.isEmpty(leaveTimeMap.get(list.get(0).getUserId()).get(dList.get(0).getSchDate()))){
                    checkinDay.setLeaveTimeDetails(leaveTimeMap.get(list.get(0).getUserId()).get(dList.get(0).getSchDate()));
                }
                checkinDays.add(checkinDay);
            });
            checkinDays.sort(Comparator.comparing(CheckinDay::getCheckinDays));
            checkinRespVO.setCheckinDays(checkinDays);
            respVOS.add(checkinRespVO);
        });
        return respVOS;
    }

    private List<ApprovedResp> getApproved(List<String> users) {
        ApprovedReqVO vo = new ApprovedReqVO();
        vo.setProcessKeys(ProcessEnum.getAllTypes());
        vo.setUsers(users);
        return approvedApi.selectApproved(vo);
    }

}
